/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.octopeer.main;

import java.util.ArrayList;
import net.octopeer.core.Cache;
import net.octopeer.core.Neighbor;
import net.octopeer.core.Peer;
import net.octopeer.core.PeerNode;
import net.octopeer.core.Resource;
import net.octopeer.message.Announcement;
import net.octopeer.message.Request;
import net.octopeer.message.Respond;
import net.octopeer.network.Talker;

/**
 *
 * @author andria
 */
public class OctopeerService {
    private Peer thisPeer;
    
    public OctopeerService(Peer thisPeer) {
        this.thisPeer = thisPeer;
    }
    
    public void resourceAddService(Resource newResource) throws Exception {
        thisPeer.addResource(newResource);
        System.out.println("resource added: " + newResource.getResourceID());
        
        // announce to all neighbors
        Announcement anno = new Announcement(thisPeer.getNode(), thisPeer.getResourceList());
        String annoMsg = anno.toXMLString();
        int neighborLoop = 0;
        
        while (neighborLoop < thisPeer.getNeighborList().size()) {
            PeerNode neighborNode = (PeerNode) thisPeer.getNeighborList().get(neighborLoop).getNode();
            Talker t = new Talker(neighborNode.getNodeIPaddress(), neighborNode.getNodePort());
            t.sendMessage(annoMsg);
            neighborLoop++;
        }
    }
    
    public void resourceDelService(Resource delResource) throws Exception {
        thisPeer.removeResource(delResource);
        System.out.println("resource removed: " + delResource.getResourceID());
        
        // announce to all neighbors
        Announcement anno = new Announcement(thisPeer.getNode(), thisPeer.getResourceList());
        String annoMsg = anno.toXMLString();
        int neighborLoop = 0;
        
        while (neighborLoop < thisPeer.getNeighborList().size()) {
            PeerNode neighborNode = (PeerNode) thisPeer.getNeighborList().get(neighborLoop).getNode();
            Talker t = new Talker(neighborNode.getNodeIPaddress(), neighborNode.getNodePort());
            t.sendMessage(annoMsg);
            neighborLoop++;
        }
    }
    
    public void resourceListService() {
        int resourceLoop = 0;
        while (resourceLoop < thisPeer.getResourceList().size()) {
            Resource res = (Resource) thisPeer.getResourceList().get(resourceLoop);
            System.out.println(resourceLoop + ":" + res.getResourceID());
            resourceLoop++;
        }
    }
    
    public void neighborAddService(PeerNode newNeighbor) {
        thisPeer.addNeighborNode(newNeighbor);
        System.out.println("Neighbor added: " + newNeighbor.getNodeIPaddress());
    }
    
    public void neighborDelService(PeerNode delNeighbor) {
        thisPeer.removeNeighborNode(delNeighbor);
        System.out.println("Neighbor removed: " + delNeighbor.getNodeIPaddress());
    }
    public void neighborListService() {
        int neighborLoop = 0;
        while (neighborLoop < thisPeer.getNeighborList().size()) {
            Neighbor tempNeighbor = (Neighbor) thisPeer.getNeighborList().get(neighborLoop);
            System.out.println(tempNeighbor.getNode().getNodePort() + ":" + tempNeighbor.getRank());
            neighborLoop++;
        }
    }
    
    public void requestService(String reqString) throws Exception {
        Request req = new Request();
        req = req.fromXMLString(reqString);
        Respond rep = null;
        Resource res = null;
        Cache cach = null;
        String fwdString = "";
        String repString = "";
        
        if (thisPeer.findRequestIDIdx(req.getReqID()) == -1) {
            int resListIdx = thisPeer.findResourceIdxByKeyword(req.getReqType(), req.getReqKeyword());
            int cacheListIdx = thisPeer.findCacheIdxByKeyword(req.getReqType(), req.getReqKeyword());
            if (resListIdx != -1) {
                // check the internal resourcelist
                res = (Resource) thisPeer.getResourceList().get(resListIdx);
                
                // the last peerNode on pathPeers is this node, it send to the second last peerNode
                PeerNode dstPeer = (PeerNode) req.getPathPeers().get(req.getPathPeers().size() - 2);

                rep = new Respond(req.getReqID(), res, thisPeer.getNode(), req.getPathPeers());
                repString = "respond " + rep.toXMLString();

                forwardService(dstPeer, repString);
            } else if (req.getReqTtl() > 0) {
                if (cacheListIdx != -1) {
                    // check the internal cache list
                    cach = (Cache) thisPeer.getCacheList().get(cacheListIdx);
                    // forward to peers which has not been included in pathPeers
                    int fwdNodeLoop = 0;
                    while (fwdNodeLoop < cach.getNodeList().size()) {
                        PeerNode fwdNode = (PeerNode) cach.getNodeList().get(fwdNodeLoop);

                        int peerNodePathIdx = req.findPathPeersNodeIdx(fwdNode);
                        if (peerNodePathIdx == -1) {
                            Request newReq = new Request(req);
                            newReq.addPathPeer(fwdNode);
                            newReq.decReqTtl();
                            fwdString = "request " + req.toXMLString();
                            forwardService(fwdNode, fwdString);
                        }

                        fwdNodeLoop++;
                    }
                } else {
                    // if not exist in requestID list, forwardService
                    int neighNodeLoop = 0;
                    while (neighNodeLoop < thisPeer.getNeighborList().size()) {
                        PeerNode neighNode = (PeerNode) thisPeer.getNeighborList().get(neighNodeLoop).getNode();

                        int peerNodePathIdx = req.findPathPeersNodeIdx(neighNode);
                        if (peerNodePathIdx == -1) {
                            Request newReq = new Request(req);
                            newReq.addPathPeer(neighNode);
                            newReq.decReqTtl();
                            fwdString = "request " + req.toXMLString();
                            forwardService(neighNode, fwdString);
                        }

                        neighNodeLoop++;
                    }
                }
            } else {
                System.out.println("Request message droped, exceeded TTL");
            }
        } else {
            // else drop.
            System.out.println("Request message droped.");
        }
    }
    
    public void respondService(String repString) throws Exception {
        Respond rep = new Respond();
        rep = rep.fromXMLString(repString);
        PeerNode reqNode = rep.getPathPeers().get(0);
        Cache resCache = null;
        
        if (thisPeer.getNode().getNodeID().equals(reqNode.getNodeID())) {
            // if this node is the requester (respond destination),
            // it adds the resource infomation to its cache
            // and inform that resource is found
            Resource res = rep.getResDetail();
            System.out.println(rep.getReqID() + " is received...");
            System.out.println(res.fromXMLString(repString));
            resCache = new Cache(res, rep.getResNode());
            thisPeer.addCache(resCache);
            
            // update Neighbor information
            PeerNode neighRepNode = rep.getPathPeers().get(rep.getPathPeers().size()-1);
            thisPeer.addNeighborNode(neighRepNode);
        } else {
            // update internal cache list
            Resource res = rep.getResDetail();
            resCache = new Cache(res, rep.getResNode());
            thisPeer.addCache(resCache);
            
            // update Neighbor information
            PeerNode neighRepNode = rep.getPathPeers().get(rep.getPathPeers().size()-1);
            thisPeer.addNeighborNode(neighRepNode);
            
            // forwardservice
            rep.remPathPeer();
            PeerNode dstPeer = rep.getPathPeers().get(rep.getPathPeers().size() - 2);
            String fwdString = "respond " + rep.toXMLString();
            forwardService(dstPeer, fwdString);
        }
    }
    
    public void forwardService(PeerNode destPeer, String fwdString) throws Exception {
        Talker talk = new Talker();
        talk.connect(destPeer.getNodeIPaddress(), destPeer.getNodePort());
        talk.sendMessage(fwdString);
    }
    
//    public void neighborPingService(String svcString) {
//        //
//    }
}
